iT邦幫忙

2025 iThome 鐵人賽

DAY 29
0
Security

「站住 口令 誰」關於資安權限與授權的觀念教學,以Spring boot Security框架實作系列 第 29

Day 29:專案案例 Part 3 — 進階授權策略與最佳實務

  • 分享至 

  • xImage
  •  

在前一篇(Day 28)我們整合了 RBAC + ABAC,今天要更進一步,將 Scope(範圍控制) 也引入授權邏輯,並討論一些專案中的最佳實務做法。

一、進階授權策略的構成

今天的案例同時考慮三個層面:

  1. RBAC(角色)
    • 使用者必須有特定角色,例如 ROLE_ADMIN
  2. ABAC(屬性)
    • 使用者必須屬於特定部門,例如 FINANCE
  3. Scope(範圍)
    • Token 必須攜帶特定操作範圍,例如 readwrite

這三者加在一起,就能實現更精細的授權策略。

二、程式碼解析

1. AuthController

// RBAC: 只有 ROLE_ADMIN 可進
@GetMapping("/admin")
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
public String adminOnly() {
    return "這是管理員專區 (RBAC)";
}

// ABAC + Scope + Role: 三重檢查
@GetMapping("/finance/report")
@PreAuthorize("hasAuthority('ROLE_ADMIN') and @scopeSecurity.hasScope(authentication, 'read') and @departmentSecurity.checkDepartment(authentication, 'FINANCE')")
public String financeReport() {
    return "這是財務報表,只有 Finance 部門 + Admin + 有 read scope 才能看 (Scope+Role+Attribute)";
}

  • /admin → 單純 RBAC
  • /finance/report → 必須同時符合 角色 + Scope + 部門

2. JwtAuthenticationFilter

String role = claims.get("role", String.class);
String scope = claims.get("scope", String.class);

UsernamePasswordAuthenticationToken authentication =
    new UsernamePasswordAuthenticationToken(
        username,
        null,
        List.of(new SimpleGrantedAuthority(role))
    );

authentication.setDetails(Map.of("scope", scope));
SecurityContextHolder.getContext().setAuthentication(authentication);

  • JWT 除了角色,還加入了 scope
  • 這裡把 scope 放進 authentication.setDetails(),讓後續的授權判斷可以讀取。

3. JwtUtil

public static String generateAccessToken(String username, String role, String scope) {
    return Jwts.builder()
            .setSubject(username)
            .addClaims(Map.of("role", role, "scope", scope))
            .setIssuedAt(new Date())
            .setExpiration(new Date(System.currentTimeMillis() + 60 * 1000)) // 1 分鐘
            .signWith(key, SignatureAlgorithm.HS256)
            .compact();
}

  • Admin 帳號 → ROLE_ADMIN + read write
  • User 帳號 → ROLE_USER + read

4. ScopeSecurity(自訂 Bean,用於檢查 scope)

package com.ansathsean.security;

import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Component;

@Component("scopeSecurity")
public class ScopeSecurity {

    public boolean hasScope(Authentication authentication, String requiredScope) {
        Object details = authentication.getDetails();
        if (details instanceof java.util.Map<?, ?> map) {
            String scopes = (String) map.get("scope");
            if (scopes != null) {
                for (String s : scopes.split(" ")) {
                    if (s.equals(requiredScope)) {
                        return true;
                    }
                }
            }
        }
        return false;
    }
}

  • 檢查 Token 是否包含所需的 scope,例如 read
  • 這樣可以在 @PreAuthorize 中靈活使用。

三、最佳實務建議

  1. 最小權限原則(Principle of Least Privilege)
    • 預設不要給使用者超過需求的角色與 Scope。
    • 例如:一般使用者應該只擁有 read,不該擁有 write
  2. 結合多層策略
    • RBAC:角色決定大方向。
    • Scope:決定功能範圍(讀、寫)。
    • ABAC:細化到屬性,例如部門、資源擁有者。
    • 三者結合可避免「角色過於龐大」或「權限管理失控」。
  3. Token 設計建議
    • role:放置在 JWT claims。
    • scope:以空白分隔的字串,方便解析。
    • attribute:如 department,可以來自 DB 或 OIDC Claim。
  4. 過期時間與 Refresh Token
    • 短效的 Access Token + Refresh Token → 提升安全性。
    • 過期後必須重新驗證,而不是無限期使用。
  5. 集中策略管理
    • 若專案持續擴張,可以考慮外部策略引擎(如 OPA, XACML)。
    • 讓授權規則統一管理,而不是分散在程式碼裡。

四、測試情境

  1. Admin 帳號 + Finance 部門 + read scope
    • ✅ 可以存取 /finance/report
  2. Admin 帳號 + Finance 部門,但缺少 read scope
    • ❌ 被拒絕。
  3. User 帳號(ROLE_USER + read scope)
    • ❌ 沒有 ROLE_ADMIN → 被拒絕。

到了這裡,我們完成了 進階授權策略,將 Role + Scope + Attribute 三者結合,實現真正靈活的存取控制。

這樣的設計更貼近真實世界的需求,例如「某部門主管才能審核報表」、「特定 API 僅能在工作時段使用」。這也代表我們將全部的授權與認證的知識都交給大家了!

感謝大家的觀看,不知不覺也已經過了29天,相信大家對於授權與認證的情境了解得更多,我自己在開發的過程中也學到了很多,身為一個工程師,學到最多東西的時段絕對不是開發(畢竟現在有AI在,開發輕鬆很多),最重要的還是在測試的過程中,在試誤的過程中學到更多扎實的內容。希望我學到的這些知識能夠成為大家的養分~

明天就是最後一天,我會整理 Spring Security 實戰 30 天學習總結,寫一些我的心得,非常感謝大家的收看,那我們,明天見!


上一篇
Day 28:專案案例 Part 2 — 加入授權邏輯 (RBAC + ABAC)
下一篇
Day 30:總結與未來趨勢
系列文
「站住 口令 誰」關於資安權限與授權的觀念教學,以Spring boot Security框架實作30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言